package org.bing.engine.console.service;

import java.util.ArrayList;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import org.bing.engine.console.remote.RemoteFactory;
import org.bing.engine.core.domain.Console;
import org.bing.engine.core.domain.Controller;
import org.bing.engine.core.domain.ControllerGroup;
import org.bing.engine.core.domain.Domain;
import org.bing.engine.core.service.ControllerLifecycle;
import org.hibernate.HibernateException;
import org.hibernate.Query;
import org.hibernate.Session;
import org.springframework.orm.hibernate3.HibernateCallback;

public class ControllerManager extends AbstractHibernateManager {
    private Map<String, Controller> tmpControllers = new ConcurrentHashMap<String, Controller>();
    private long maxTmpTimeout = 1000 * 30; // timeout 30 S
    private ConsoleNodeGenerator nodeGenerator;
    private RemoteFactory remoteFactory;

    public void setRemoteFactory(RemoteFactory factory) {
        this.remoteFactory = factory;
    }

    public void setNodeGenerator(ConsoleNodeGenerator generator) {
        this.nodeGenerator = generator;
    }

    public void multicast(Controller controller) {
        controller.setLastActivityTime(new Date());
        String guid = controller.getGlobalId();
        if (!tmpControllers.containsKey(guid)) {
            logger.info("controller " + guid + " add into temp group.");
        }
        tmpControllers.put(guid, controller);
    }

    @SuppressWarnings("unchecked")
    public Controller findByGlobalId(String globalId) {
        List<Controller> res = template.find("select c from Controller c where c.globalId=?", globalId);
        if (res != null && res.size() > 0) {
            if (res.size() > 1) {
                logger.error("Find over 1 controller by " + globalId);
                throw new RuntimeException("Find over 1 controller by " + globalId);
            } else {
                return res.get(0);
            }
        }
        return null;
    }

    public void save(Controller controller) {
        template.saveOrUpdate(controller);
    }

    public void delete(Controller controller) {
        template.delete(controller);
    }
    
    public void addToGroup(ControllerGroup group, List<Controller> controllers) {
        Console console = nodeGenerator.getThisConsole();
        for (Controller controller : controllers) {
            // use java rpc on remoting, maybe DSL is better?
            ControllerLifecycle lifecycle = remoteFactory.getRemoteService(controller, ControllerLifecycle.class);
            lifecycle.join(console, group);
            tmpControllers.remove(controller.getGlobalId());
        }
    }

    @SuppressWarnings("unchecked")
    public List<Controller> findAll() {
        List<Controller> list = template.find("select t from Controller t");
        return list;
    }

    @SuppressWarnings({ "unchecked", "rawtypes" })
    public List<Controller> findByDomain(Domain dom) {
        // TODO impl by base dao
        final List<ControllerGroup> grps = template.find("select t.group from ControllerGroupOfDomain t where t.domain=?", dom);
        if (grps == null || grps.size() == 0) {
            return new ArrayList<Controller>(1);
        }

        final String hql = "select t from Controller t where t.group in (:group)";
        return (List) template.execute(new HibernateCallback() {
            public Object doInHibernate(Session session) throws HibernateException {
                Query query = session.createQuery(hql);
                query.setParameterList("group", grps);
                return query.list();
            }
        });
    }

    public List<Controller> findTmpController() {
        Iterator<String> itr = tmpControllers.keySet().iterator();
        long now = System.currentTimeMillis();
        while (itr.hasNext()) {
            String guid = itr.next();
            Controller c = tmpControllers.get(guid);
            if (now - c.getLastActivityTime().getTime() > maxTmpTimeout) {
                tmpControllers.remove(guid);
            }
        }
        return new ArrayList<Controller>(tmpControllers.values());
    }
}
